package com.cloud.tool.service;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import java.time.Duration;

/**
 * 二级缓存工具类
 *
 * @author WangZY
 **/
@Component
@Slf4j
@ConditionalOnProperty(name = "tool.redis-enable", havingValue = "true", matchIfMissing = true)
public class CachePlusTool {
    @Autowired
    public RedissonClient redissonClient;
    /**
     * 本地缓存设置时不需要太长TTL以及太大容量，会占用过多内存造成OOM，得不偿失
     * 设置初始容量为1000，最大容量为10000，普通业务足够，对于过多元素的可采用json压缩为一个元素
     * 设置默认超时时间为4小时的考虑是经验值，过长的缓存没有意义，用户几乎在白天操作，且夜晚很多项目需要跑定时，会占用大量内存
     */
    private final Cache<String, Object> caffeine = Caffeine.newBuilder()
            .initialCapacity(1000).expireAfterAccess(Duration.ofHours(4))
            .maximumSize(10000).build();

    /**
     * 缓存基本的对象，Integer、String、实体类等，默认超时时间4小时
     *
     * @param key   缓存的键值
     * @param value 缓存的值
     */
    public <T> void put(String key, T value) {
        try {
            redissonClient.getBucket(key).set(value, Duration.ofHours(4));
        } catch (Exception e) {
            log.error("Redis的Put连接失败，降级处理使用本地缓存Caffeine", e);
            caffeine.put(key, value);
        }
    }

    /**
     * 重载版本，仅支持对redis的ttl设置，对caffeine无效
     *
     * @param key     缓存的键值
     * @param value   缓存的值
     * @param timeout 过期时间
     */
    public <T> void put(String key, T value, Duration timeout) {
        try {
            redissonClient.getBucket(key).set(value, timeout);
        } catch (Exception e) {
            log.error("Redis的Put连接失败，降级处理使用本地缓存Caffeine", e);
            caffeine.put(key, value);
        }
    }

    /**
     * 获得缓存的基本对象。
     * 可能返回null值--因为对于缓存来说类似于数据库，分不清是程序错误导致的null还是数据为空的null，所以需要开发人员自行判断
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public <T> T get(String key) {
        Object value = null;
        try {
            value = redissonClient.getBucket(key).get();
        } catch (Exception e) {
            log.error("Redis的Get连接失败，降级处理使用本地缓存Caffeine", e);
            //没有返回null
            value = caffeine.getIfPresent(key);
        }
        return (T) value;
    }
}